package cn.wenwuyi.blockchain.pojo;

import java.util.ArrayList;
import java.util.List;

import cn.wenwuyi.blockchain.pojo.Block;
import cn.wenwuyi.blockchain.util.CryptoUtil;

/**
 * 
 * 类名：BlockService.java
 * 描述：区块服务
 * 时间：2018年3月12日 下午7:05:06 
 * @author cn.wenwuyi
 * @version 1.0
 */
public class BlockChain {
	/**
	 * 区块链定义 其实就是一个list
	 */
    private List<Block> blockChain;
    /**
     * 区块链服务
     * 1、初始化blockchain
     * 2、区块链加入创世区块
     */
    public BlockChain() {
        this.blockChain = new ArrayList<Block>();
        blockChain.add(this.getFristBlock());
    }
    /**
     * 计算hash服务
     * @param index 索引
     * @param previousHash 前一个区块的hash值
     * @param timestamp 时间戳
     * @param data 数据
     * @return 当前hash
     */
    private String calculateHash(int index, String previousHash, long timestamp, String data) {
        StringBuilder builder = new StringBuilder(index);
        builder.append(previousHash).append(timestamp).append(data);
        return CryptoUtil.getSHA256(builder.toString());
    }
    /**
     * 获取最后一个区块
     * @return 返回区块内容
     */
    public Block getLatestBlock() {
        return blockChain.get(blockChain.size() - 1);
    }
    /**
     * 获取-创世区块
     * @return
     */
    private Block getFristBlock() {
        return new Block(1, "0", 1521339156921l, "Hello Block", "aa212344fc10ea0a2cb885078fa9bc2354e55efc81be8f56b66e4a837157662e");
    }
    /**
     * 生成下一个区块
     * @param blockData
     * @return Block
     */
    public Block generateNextBlock(String blockData) {
    	//获得前一个区块
        Block previousBlock = this.getLatestBlock();
        //区块的索引加1
        int nextIndex = previousBlock.getIndex() + 1;
        //现在的时间戳
        long nextTimestamp = System.currentTimeMillis();
        //计算hash值
        String nextHash = calculateHash(nextIndex, previousBlock.getHash(), nextTimestamp, blockData);
        //返回生成的新区块
        return new Block(nextIndex, previousBlock.getHash(), nextTimestamp, blockData, nextHash);
    }
    /**
     * 添加区块
     * @param newBlock
     */
    public void addBlock(Block newBlock) {
        if (isValidNewBlock(newBlock, getLatestBlock())) {
            blockChain.add(newBlock);
        }
    }
    /**
     * 检查区块是否新区块
     * @param newBlock
     * @param previousBlock
     * @return true/false
     */
    private boolean isValidNewBlock(Block newBlock, Block previousBlock) {
        if (previousBlock.getIndex() + 1 != newBlock.getIndex()) {//1、前一个区块的索引加1不等于新区块
            System.out.println("invalid index");
            return false;
        } else if (!previousBlock.getHash().equals(newBlock.getPreviousHash())) {//前一个区块的hash不等于新区块中存储的前一个区块的hash
            System.out.println("invalid previoushash");
            return false;
        } else {
        	//计算hash
            String hash = calculateHash(newBlock.getIndex(), newBlock.getPreviousHash(), newBlock.getTimestamp(),
                    newBlock.getData());
            //如果hash和计算后的hash不相等
            if (!hash.equals(newBlock.getHash())) {
                System.out.println("invalid hash: " + hash + " " + newBlock.getHash());
                return false;
            }
        }
        return true;
    }
    /**
     * 新区块链替换成+1的区块链
     * @param newBlocks
     */
    public void replaceChain(List<Block> newBlocks) {
    	//如果新区块链合法并且新区块链的长度大于现有区块链的长度，则替换
        if (isValidBlocks(newBlocks) && newBlocks.size() > blockChain.size()) {
            blockChain = newBlocks;
        } else {
            System.out.println("Received blockchain invalid");
        }
    }
    /**
     * 验证区块链是否合法
     * @param newBlocks
     * @return true/false
     */
    private boolean isValidBlocks(List<Block> newBlocks) {
    	//获得第一个区块链
        Block fristBlock = newBlocks.get(0);
        //判断第一个区块是否是创世区块
        if (fristBlock.equals(getFristBlock())) {
            return false;
        }
        //循环每个验证区块是否合法，在区块链中最耗时
        for (int i = 1; i < newBlocks.size(); i++) {
            if (isValidNewBlock(newBlocks.get(i), fristBlock)) {
                fristBlock = newBlocks.get(i);
            } else {
                return false;
            }
        }
        return true;
    }
    /**
     * 获得区块链
     * @return
     */
    public List<Block> getBlockChain() {
        return blockChain;
    }
}
